import { Node, TiledMap, UITransform, v2, v3, Vec3 } from "cc";
import { app } from "db://app-game/core/app";
import { ecs } from "db://app-game/libs/ecs/ECS";
import { MonitorGroupComp } from "../../common/ecs/model/MonitorGroupComp";
import { StaticCreature } from "../static-creature/StaticCreature";
import {
  MapBaseInfo,
  MapModelComp,
  ObstacleBaseInfo,
} from "./model/MapModelComp";
import { MapRenderModelComp } from "./model/MapRenderModelComp";
import { smc } from "../../common/ecs/model/SingletonModuleComp";

@ecs.register("MapE")
export class MapE extends ecs.Entity {
  // 数据层
  MapModel!: MapModelComp;

  // 业务层
  MapRenderModel!: MapRenderModelComp;
  MonitorGroup!: MonitorGroupComp;
  //节点
  /** 地图 */
  map: Node;
  /** 静态生物 */
  staticCreature: Node;
  /** 怪兽工厂节点 */
  monsterFactoryNode: Node;
  init() {
    this.add(MapModelComp);
  }

  /** 加载初始地图 */
  loadInitMap(id: number) {
    this.MapModel.id = id;

    // 2.添加地图节点
    this.createNode();

    // 3.地图渲染
    this.mapRender();

    // 4.添加组件
    this.add(MapRenderModelComp);
    this.add(MonitorGroupComp);

    this.MonitorGroup.group = [
      {
        name: this.staticCreature.name,
        monitorNode: this.staticCreature,
      },
      {
        name: this.monsterFactoryNode.name,
        monitorNode: this.monsterFactoryNode,
      },
    ];
  }

  createNode() {
    // 1.创建地图节点
    this.map = new Node();
    this.map.name = "map";
    this.map.addComponent(UITransform);
    this.map.parent = app.gui.game;

    // 2.创建静态生物节点
    this.staticCreature = new Node();
    this.staticCreature.name = "static-creature";
    this.staticCreature.addComponent(UITransform);
    this.staticCreature.parent = app.gui.game;

    // 3.添加怪兽工厂节点
    this.monsterFactoryNode = new Node();
    this.monsterFactoryNode.name = "monster-factory";
    this.monsterFactoryNode.addComponent(UITransform);
    this.monsterFactoryNode.parent = app.gui.game;
  }

  /** 地图渲染 */
  mapRender() {
    const { chunkMapWidth, chunkMapHeight } = this.MapModel;
    const { mapFilePath, initPos, mapTable } = this.MapModel.data;

    // 循环渲染地图
    for (let row = 0; row < mapTable.length; row++) {
      for (let col = 0; col < mapTable[row].length; col++) {
        // 1.创建地图节点
        const mapIndex = { row, col };
        const node = this.MapModel.db.createMapNode(mapFilePath, mapIndex);
        node.name = `${row}-${col}-map`;

        // 2.设置地图位置
        const mapX = (col - initPos.col) * chunkMapWidth;
        const mapY = -(row - initPos.row) * chunkMapHeight;
        node.setPosition(mapX, mapY);

        // 3.设置地图父级节点
        node.parent = this.map;
        const tiledMap = node.getComponent(TiledMap);

        // 4.设置地图基础信息
        const mapInfo = {
          id: `${row}-${col}`,
          pos: v2(mapX, mapY),
          minX: mapX - chunkMapWidth / 2,
          maxX: mapX + chunkMapWidth / 2,
          minY: mapY - chunkMapHeight / 2,
          maxY: mapY + chunkMapHeight / 2,
          rendering: false,
        };
        this.MapModel.mapNodesMap.set(mapInfo.id, mapInfo);

        // 5.添加静态障碍物渲染
        this.obstacleRender(tiledMap, mapInfo);
        // 6.添加对象渲染
        this.objectRender(node, tiledMap, mapInfo);
      }
    }
  }

  /** 障碍物渲染 */
  obstacleRender(tiledMap: TiledMap, mapInfo: MapBaseInfo) {
    // 1.获取地图障碍物层级
    const layerObstacle = tiledMap.getLayer("obstacle");
    if (!layerObstacle) return;

    // 2.获取障碍物基础信息
    const obstacleSize = layerObstacle.getLayerSize();
    let mapSize = layerObstacle.getMapTileSize();
    const obstaclesMap = new Map();
    // 3.遍历瓷砖每一块
    for (let x = 0; x < obstacleSize.width; x++) {
      for (let y = 0; y < obstacleSize.height; y++) {
        // 3.1判断当前块是否有属性
        const tiled = layerObstacle.getTiledTileAt(x, y, true);
        // 3.2判断是否有grid
        if (tiled.grid === 0) continue;

        // 3.3配置障碍物基础信息
        const pos = v2(
          mapInfo.minX + mapSize.width * x + mapSize.width / 2,
          mapInfo.maxY - mapSize.height * y - mapSize.height / 2
        );
        const obstacleInfo = {
          id: `${mapInfo.id}-${x}-${y}`,
          pos,
          minX: pos.x - mapSize.width / 2,
          maxX: pos.x + mapSize.width / 2,
          minY: pos.y - mapSize.height / 2,
          maxY: pos.y + mapSize.height / 2,
        };
        obstaclesMap.set(`${x}-${y}`, obstacleInfo);
      }
    }
    this.MapModel.obstacleMap.set(`${mapInfo.id}`, obstaclesMap);
  }

  /** 添加对象 */
  objectRender(node: Node, tiledMap: TiledMap, mapInfo: MapBaseInfo) {
    // 1.获取静态生物点位
    let staticCreatureGroup = tiledMap.getObjectGroup("staticCreature");
    if (!staticCreatureGroup) return;
    let staticCreatureObjects = staticCreatureGroup.getObjects();

    // 2.创建静态生物预制体
    for (let i = 0; i < staticCreatureObjects.length; i++) {
      // 2.1获取静态生物信息
      let staticCreatureObject = staticCreatureObjects[i] as any;

      // 2.2创建静态生物实体
      const staticCreature = ecs.getEntity(StaticCreature);
      const pos = v3(
        mapInfo.minX + staticCreatureObject.offset.x,
        mapInfo.maxY - staticCreatureObject.offset.y
      );
      staticCreature.StaticModel.id =
        staticCreatureObject.staticCreatureType as number;
      staticCreature.load(this.staticCreature, pos);

      // 2.3添加静态生物信息
      if (this.MapModel.staticCreatureMap.has(mapInfo.id)) {
        const staticCreatures = this.MapModel.staticCreatureMap.get(mapInfo.id);
        staticCreatures.push(staticCreature);
        this.MapModel.staticCreatureMap.set(`${mapInfo.id}`, staticCreatures);
      } else {
        this.MapModel.staticCreatureMap.set(`${mapInfo.id}`, [staticCreature]);
      }
    }
  }

  /** 计算地图索引 */
  computedMapIndex(
    pos: Vec3,
    velocity: Vec3
  ): { mapInfo: MapBaseInfo; velocity: Vec3 } {
    const { chunkMapWidth, chunkMapHeight } = this.MapModel;
    const newPos = v2(
      pos.x + velocity.x + chunkMapWidth / 2,
      pos.y + velocity.y + chunkMapHeight / 2
    );
    const offsetRow = -Math.floor(newPos.y / chunkMapHeight);
    const offsetCol = Math.floor(newPos.x / chunkMapWidth);

    const row = this.MapModel.data.initPos.row + offsetRow;
    const col = this.MapModel.data.initPos.col + offsetCol;

    // 前进地图x和y都没超出地图
    if (this.MapModel.mapNodesMap.has(`${row}-${col}`)) {
      return {
        mapInfo: this.MapModel.mapNodesMap.get(`${row}-${col}`),
        velocity: velocity,
      };
    }

    // 计算以前没超出时候的位置
    const oldPos = v2(newPos.x - velocity.x, newPos.y - velocity.y);
    const oldOffsetRow = -Math.floor(oldPos.y / chunkMapHeight);
    const oldOffsetCol = Math.floor(oldPos.x / chunkMapWidth);

    const oldRow = this.MapModel.data.initPos.row + oldOffsetRow;
    const oldCol = this.MapModel.data.initPos.col + oldOffsetCol;
    // 移动速度
    let x = velocity.x;
    let y = velocity.y;
    // 检查x轴是否超出
    if (this.MapModel.mapNodesMap.has(`${row}-${oldCol}`)) {
      const mapInfo = this.MapModel.mapNodesMap.get(`${row}-${oldCol}`);
      if (velocity.x < 0) {
        x = mapInfo.minX - oldPos.x + chunkMapWidth / 2 + 2;
      } else {
        x = mapInfo.maxX - oldPos.x + chunkMapWidth / 2 - 2;
      }
      return {
        mapInfo,
        velocity: v3(x, y, 0),
      };
    }

    // 检查y抽是否超出
    if (this.MapModel.mapNodesMap.has(`${oldRow}-${col}`)) {
      const mapInfo = this.MapModel.mapNodesMap.get(`${oldRow}-${col}`);
      if (velocity.y < 0) {
        y = mapInfo.minY - oldPos.y + chunkMapHeight / 2 + 2;
      } else {
        y = mapInfo.maxY - oldPos.y + chunkMapHeight / 2 - 2;
      }
      return {
        mapInfo,
        velocity: v3(x, y, 0),
      };
    }

    // x,y都超了，
    const mapInfo = this.MapModel.mapNodesMap.get(`${oldRow}-${oldCol}`);
    if (velocity.x < 0) {
      x = mapInfo.minX - oldPos.x + chunkMapWidth / 2 + 2;
    } else {
      x = mapInfo.maxX - oldPos.x + chunkMapWidth / 2 - 2;
    }
    if (velocity.y < 0) {
      y = mapInfo.minY - oldPos.y + chunkMapHeight / 2 + 2;
    } else {
      y = mapInfo.maxY - oldPos.y + chunkMapHeight / 2 - 2;
    }
    return {
      mapInfo,
      velocity: v3(x, y, 0),
    };
  }

  /** 计算地图瓷砖索引 */
  conmputedTiledIndex(
    pos: Vec3,
    velocity: Vec3,
    mapInfo: MapBaseInfo
  ): {
    obstacle: ObstacleBaseInfo;
    velocity: Vec3;
  } {
    const newPos = v2(pos.x + velocity.x, pos.y + velocity.y);
    const { tileWidth, tileHeight } = this.MapModel;
    const { minX, maxY } = mapInfo;
    const relativeY = maxY - newPos.y;
    const relativeX = newPos.x - minX;

    // 移动速度
    let x = velocity.x;
    let y = velocity.y;
    const oldRelativeY = maxY - pos.y;
    const oldRelativeX = pos.x - minX;
    const oldRow = Math.floor(oldRelativeY / tileHeight);
    const oldCol = Math.floor(oldRelativeX / tileWidth);

    const row = Math.floor(relativeY / tileHeight);
    const col = Math.floor(relativeX / tileWidth);
    const offsetVal = 4;

    // 检查x轴是否超出
    if (this.MapModel.obstacleMap.has(`${mapInfo.id}`)) {
      const obstaclesMap = this.MapModel.obstacleMap.get(`${mapInfo.id}`);
      if (obstaclesMap.has(`${col}-${oldRow}`)) {
        if (x < 0) {
          x = x - (relativeX - tileWidth * (col + 1)) + offsetVal;
        } else {
          x = x - (relativeX - tileWidth * col) - offsetVal;
        }
      }
    }

    // 检查y轴是否超出
    if (this.MapModel.obstacleMap.has(`${mapInfo.id}`)) {
      const obstaclesMap = this.MapModel.obstacleMap.get(`${mapInfo.id}`);
      if (obstaclesMap.has(`${oldCol}-${row}`)) {
        if (y < 0) {
          y = y - (tileHeight * row - relativeY) + offsetVal;
        } else {
          y = y - (tileHeight * row + tileHeight - relativeY) - offsetVal;
        }
      }
    }
    return {
      obstacle: null,
      velocity: v3(x, y, 0),
    };
  }

  /** 重置速度 */
  resetVelocity(pos: Vec3, velocity: Vec3) {
    let v = velocity;
    // 1.把位置更改到地图上面的位置
    const nPos = this.map.getComponent(UITransform).convertToNodeSpaceAR(pos);

    // 3.计算出你在那块地图上
    const { mapInfo, velocity: newVelocity } = this.computedMapIndex(
      nPos,
      velocity
    );
    // 3.1更新移动速度
    v = newVelocity;

    // 4.计算出你准备去那块瓷砖
    const { obstacle, velocity: obstacleVelocity } = this.conmputedTiledIndex(
      nPos,
      v,
      mapInfo
    );
    // 4.1更新移动速度
    v = obstacleVelocity;

    // 5.碰到静态物品不能移动
    if (this.MapModel.staticCreatureMap.has(mapInfo.id)) {
      // 5.1获取静态生物信息
      const newPos = v2(pos.x + velocity.x, pos.y + velocity.y);
      const staticCreatures = this.MapModel.staticCreatureMap.get(mapInfo.id);
      const staticIndex = staticCreatures.findIndex((staticCreature) => {
        // 5.2获取静态生物位置信息
        const pos = staticCreature.StaticModel.node.position;
        // 5.3获取碰撞盒信息
        const { width, height, offset } =
          staticCreature.StaticModel.data.collisionBox;
        const x = pos.x + offset.x;
        const y = pos.y + offset.y;

        // 5.4判断是否碰撞
        if (
          newPos.x > x - width / 2 &&
          newPos.x < x + width / 2 &&
          newPos.y > y - height / 2 &&
          newPos.y < y + height / 2
        ) {
          return true;
        }
        return false;
      });
      if (staticIndex !== -1) return v3(0, 0, 0);
    }
    return v;
  }

  // 设置渲染跟随目标
  setfollowTarget(target: Node) {
    this.MapRenderModel.target = target;
  }
}
